# 4.切片 list tuple str
import dis
import bisect
import random
import sys
from array import array
import numpy
from collections import deque

s = 'bicycle'
print(s[::3])
print(s[::-1])
print(s[::-2])

invoice = """
0.....6................................40........52...55........
1909  Pimoroni PiBrella                    $17.50    3    $52.50
1489  6mm Tactile Switch x20                $4.95    2    $9.90
1510  Panavise Jr. - PV-201                $28.00    1    $28.00
1601  PiTFT Mini Kit 320x240               $34.95    1    $34.95
"""
SKU = slice(0, 6)
DESCRIPTION = slice(6, 40)
UNIT_PRICE = slice(40, 52)
QUANTITY = slice(52, 55)
ITEM_TOTAL = slice(55, None)
line_items = invoice.split('\n')[2:-1]
for item in line_items:
    print(item[UNIT_PRICE], item[DESCRIPTION])

# 切片赋值
l = list(range(10))
print(l)
l[2:5] = [20, 30]  # 修改了原list
print(l)
del l[5:7]
print(l)
print(l[3::2])
l[3::2] = [11, 22]
print(l)
# l[2:5]=100 error can only assign an iterable
l[2:5] = [100]
print(l)

# 5.对序列用+和*
l = [1, 2, 3]
print(l * 5)  # 如果l里面是可变对象 则重复的指向的是同一个引用
print(5 * 'abcd')
board = [['_'] * 3 for i in range(3)]
print(board)
board[1][2] = 'X'
print(board)
weird_board = [['_'] * 3] * 3  # 指向同一个
print(weird_board)
weird_board[1][2] = 'O'
print(weird_board)

# 6.序列的增量赋值 +=、*=
# += 背后的特殊方法是 __iadd__ （用于“就地加法”）
# 但是如果一个类没有实现这个方法的话，Python 会退一步调用 __add__(a=a+b 重新赋值)
# *= 同理
l = [1, 2, 3]
print(id(l))
l *= 2
print(l)
print(id(l))  # id不变
t = (1, 2, 3)
print(id(t))
t *= 2
print(t)
print(id(t))  # t被重新赋值 效率低 每次都会有一个新对象然后追加元素(str有优化 例外)
# 谜题
# 不要把可变对象放在元组里面。
# 增量赋值不是一个原子操作。我们刚才也看到了，它虽然抛出了异常，但还是完成了操作。
# 查看 Python 的字节码并不难，而且它对我们了解代码背后的运行机制很有帮助。
try:
    t = (1, 2, [30, 40])
    t[2] += [50, 60]
except TypeError as e:
    pass
print(t)  # (1, 2, [30, 40, 50, 60])

# 7. list.sort 和内置函数 sorted
# 参数 reverse(True or False) key：只有一个参数的函数 如key=len key=str.lower
# list.sort 就地排序 不产生新对象
# sorted 返回新的列表 接受可迭代对象 总是返回一个列表
fruits = ['grape', 'raspberry', 'apple', 'banana']
print(sorted(fruits))
print(sorted(fruits, reverse=True))
print(sorted(fruits, key=len))
print(sorted(fruits, key=len, reverse=True))
print(fruits)
fruits.sort()
print(fruits)

# 8. bisect 管理已排序的序列
# bisect(haystack, needle) 在 haystack（干草垛）里搜索 needle（针）的位置，
# 该位置满足的条件是，把 needle 插入这个位置之后，haystack 还能保持升序
HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]
ROW_FMT = '{0:2d} @ {1:2d}    {2}{0:<2d}'


def demo(bisect_fn):
    for needle in reversed(NEEDLES):
        position = bisect_fn(HAYSTACK, needle)
        offset = position * '  |'
        print(ROW_FMT.format(needle, position, offset))


# bisect_fn = bisect.bisect_left
bisect_fn = bisect.bisect
print('DEMO:', bisect_fn.__name__)
print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))
demo(bisect_fn)


def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect(breakpoints, score)
    return grades[i]


print([grade(score) for score in [33, 99, 77, 70, 89, 90, 100]])

# insort 查找并插入item
SIZE = 7
random.seed(1729)  # 每次结果相同
my_list = []
for i in range(SIZE):
    new_item = random.randrange(SIZE * 2)
    bisect.insort(my_list, new_item)
    print('%2d ->' % new_item, my_list)

# 9.列表不是首选时
# 要存放 1000 万个浮点数的话，数组（array）的效率要高得多
# 频繁对序列做先进先出的操作，deque（双端队列）的速度应该会更快
# 包含操作（比如检查一个元素是否出现在一个集合中）的频率很高，用 set（集合）会更合适

# 数组
# 'd' 双精度浮点数
floats = array('d', (random.random() for i in range(10 ** 7)))
print(floats[-1])
fp = open('float.bin', 'wb')
floats.tofile(fp)
fp.close()
floats2 = array('d')
fp = open('float.bin', 'rb')
floats2.fromfile(fp, 10 ** 7)
fp.close()
print(floats2[-1])
print(floats2 == floats)
# 内存视图 memoryview
numbers = array('h', [-2, -1, 0, 1, 2])  # 短整型有符号整数
memv = memoryview(numbers)
print(len(memv))
print(memv[0])
memv_oct = memv.cast('B')  # 无符号字符
print(memv_oct.tolist())  # [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
memv_oct[5] = 4  # 占2个字节的整数的高位字节改成了4
print(numbers)

# Numpy 、 SciPy
a = numpy.arange(12)
print(a)
print(type(a))
print(a.shape)  # 维度(12,) 一维12个元素
a.shape = 3, 4  # 变成2维数组
print(a)
print(a[2, 1])  # 第二行第一列
print(a[:, 1])  # 第一列
print(a.transpose())  # 行转列

# 双向队列和其他形式的队列
# collections.deque类（双向队列）是一个线程安全、可以快速从两端添加或者删除元素的数据类型

dq = deque(range(10), maxlen=10)
print(dq)
dq.rotate(3)  # n>0 最右边n个元素移到左边，<0 相反
print(dq)  # [7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
dq.rotate(-4)
print(dq)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
dq.appendleft(-1)  # 0被删除
print(dq)  # [-1, 1, 2, 3, 4, 5, 6, 7, 8, 9]
dq.extend([11, 22, 33])
print(dq)  # [3, 4, 5, 6, 7, 8, 9, 11, 22, 33]
dq.extendleft([10, 20, 30, 40])  # 逐个添加到左边
print(dq)  # [40, 30, 20, 10, 3, 4, 5, 6, 7, 8]

# queue
# 提供了同步（线程安全）类 Queue、LifoQueue 和PriorityQueue，不同的线程可以利用这些数据类型来交换信息
# 如果队列满了，它就会被锁住
# multiprocessing
# 这个包实现了自己的 Queue，它跟 queue.Queue 类似，是设计给进程间通信用的。同时还有一个专门的
# multiprocessing.JoinableQueue 类型，可以让任务管理变得更方便。
# asyncio
# Python 3.4 新提供的包里面有Queue、LifoQueue、PriorityQueue 和 JoinableQueue，这些类受
# 到 queue 和 multiprocessing 模块的影响，但是为异步编程里的任务管理提供了专门的便利
# heapq
# 跟上面三个模块不同的是，heapq 没有队列类，而是提供了heappush 和 heappop 方法，
# 让用户可以把可变序列当作堆队列或者优先队列来使用
